#include "head.h"
#define LENGTH 4096

int second_hash(char *rehash, char *file_name){
    char buf[4096] = {0};
    char sha512sum[200] = {0};
    sprintf(buf,"sha512sum %s",file_name);
    FILE *pipe = popen(buf, "r");
    //FILE *pipe = popen("md5sum file.txt", "r");
    fread(sha512sum, 1, sizeof(sha512sum), pipe);
    printf("sha512sum: %s\n", sha512sum);
    strncpy(rehash, sha512sum, 128);
    printf("rehash: %s\n", rehash);
    pclose(pipe);
    return 0;
}


// client download 
int send_bigfile(int socketfd, char *file_name, char *uname, MYSQL *mysql){

    //select hash from files where uid = uname and path = file_path;
    //file_path = pwd + file_name;
    char pwd[1024] = "/";
    char file_path[1024] = {0};
    strcpy(file_path, pwd);
    printf("file_path: %s\n", file_path);
    strcat(file_path, file_name);
    printf("file_path: %s\n", file_path);
    printf("%ld\n", strlen(file_path));
    char query[4096] = {0};
    sprintf(query, "select f_hash from files where f_owner like '%s' and f_exist = 1 and f_finsh = 1 and f_path like '%s';", uname, file_path);
    printf("query: %s\n", query);
    if(mysql_query(mysql, query)) {
        printf("%s\n", mysql_error(mysql));
        return 1;
    }
    printf("query over\n");
    MYSQL_RES *result;
    result = mysql_store_result(mysql);
    printf("result over\n");
    MYSQL_ROW row;
    row = mysql_fetch_row(result);
    if(row == NULL){
        printf("客户端请求下载文件的记录不存在\n");
        int flag_exist = 0;
        send(socketfd, &flag_exist, sizeof(int), MSG_NOSIGNAL);
        return 1;
    }
    printf("row[0]: %s\n", row[0]);
    mysql_free_result(result);
    mysql_close(mysql);

    char hash[1024] = {0};
    strcpy(hash, row[0]);
    char real_path[1024] = "file/";
    strcat(real_path, hash);
    printf("real_path: %s\n", real_path);

    int fd = open(real_path, O_RDWR);
    send(socketfd, &fd, sizeof(int), MSG_NOSIGNAL);
    if(fd == -1){
        printf("服务端文件丢失\n");
        return 2;    
    }

    //获取客户端要求下载文件的大小
    struct stat stat_file;
    fstat(fd, &stat_file);
    printf("file size: %ld\n", stat_file.st_size);

    //接收客户端现有文件的大小
    off_t real_size;
    recv(socketfd, &real_size, sizeof(off_t), MSG_WAITALL);
    //printf("real size: %ld\n", real_size);

    //判断文件是否一致，一致则直接结束
    int flag_same = 0;
    char client_hash[50] = {0};
    recv(socketfd, client_hash, sizeof(client_hash), MSG_WAITALL);
    if(strcmp(hash, client_hash) == 0){
        printf("文件完全一致，不需要下载\n");
        flag_same = 1;
        send(socketfd, &flag_same, sizeof(int), MSG_NOSIGNAL);
        return 0;
    }
    send(socketfd, &flag_same, sizeof(int), MSG_NOSIGNAL);

    //position用于记录当前文件指针的位置
    //因为position要作为mmap的offset参数，所以只能是LENGTH的整数倍
    int position = (real_size/LENGTH)*LENGTH;
    //printf("position: %d\n", position);

    //每次传输的长度
    int length = LENGTH;
    //发送文件
    while(1){
        //如果总文件的大小和当前传输到的位置(positionon)相差的值小于一次传输的长度，就把长度改为这个差值
        if(stat_file.st_size - position < LENGTH){
            length = stat_file.st_size -position;
        }
        //从position的位置映射length的长度到file_read
        char *file_read = (char *)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, position);
        ERROR_CHECK(file_read, MAP_FAILED, "mmap");
        //传输内容的长度
        send(socketfd, &length, sizeof(int), MSG_NOSIGNAL);
        //printf("length = %d\n", length);
        //传输的内容
        send(socketfd, file_read, length, MSG_NOSIGNAL);
        munmap(file_read, length);
        //传输一次后，更新position的值
        position += LENGTH;
        //printf("position: %d\n", position);
        //如果position的值大于等于文件的总长度，说明position已经走到了文件的末尾
        //向服务端发送一个长度，值为0，表示传输结束
        if(position >= stat_file.st_size){
            length = 0;
            send(socketfd, &length, sizeof(int), MSG_NOSIGNAL);
            printf("文件传输完毕\n");
            break;
        }
    }
    close(fd);
    return 0;
}

int recv_bigfile(int socketfd, char *file_name, char *uname, MYSQL *mysql){
    char hash[1024] ={0};
    int ret_recv = recv(socketfd, hash, sizeof(hash), MSG_WAITALL);
    if(ret_recv == 0){
        return 1;
    }
    printf("recv hash: %s\n", hash);
    
    MYSQL_RES *result;
    MYSQL_ROW row;
    
    char pwd[1024] = "/";
    char path[1024] = {0};
    strcpy(path, pwd);
    //strcat(path, "/");
    strncat(path, file_name, 100);
    printf("path: %s\n", path);

    
    char query[4096] = {0};
    bzero(query, sizeof(query));
    sprintf(query, "select f_owner, f_path from files where f_exist = 1 and f_finsh = 1 and f_hash like '%s';", hash);
    printf("query: %s\n", query);
    if(mysql_query(mysql, query)) {
        printf("%s\n", mysql_error(mysql));
        return 1;
    }
    
    result = mysql_store_result(mysql);
    int flag_exist = 0; //判断文件是否已在服务端存在的标志
    while((row = mysql_fetch_row(result))){
        flag_exist = 1;
        //只要进到循环，就说明文件在服务端存在
        printf("row[0]: %s, row[1]: %s\n", row[0], row[1]);
        //遍历所有拥有该文件的用户，如果存在用户名和当前用户名相同，则告诉客户端已经存在该文件不需要重传
        if((strcmp(row[0], uname) == 0) && (strcmp(row[1], path) == 0)){    
            flag_exist = 2;
            send(socketfd, &flag_exist, sizeof(int), MSG_NOSIGNAL);
            printf("文件已存在\n");
            return 0;
        }
    }
    
    send(socketfd, &flag_exist, sizeof(int), MSG_NOSIGNAL);
    //如果进入该判断则说明客户端在要上传路径没有该文件，但是其他用户或该用户的其他路径拥有该文件
    if(flag_exist == 1){
        //二次哈希
        char rehash[200] = {0};
        char real_path[2048] = "file/";
        strcat(real_path, hash);
        printf("real_path: %s\n", real_path);
        second_hash(rehash, real_path);
        char client_rehash[200] = {0};
        recv(socketfd, client_rehash, sizeof(client_rehash), MSG_WAITALL);
        int flag_same = 0;
        printf("rehash: %s\n",rehash);
        printf("client_rehash: %s\n", client_rehash);
        if(strcmp(rehash, client_rehash) == 0){
            flag_same = 1;
            send(socketfd, &flag_same, sizeof(int), MSG_NOSIGNAL);        
            bzero(query, sizeof(query));
            int father_id = 0;
            //char path = pwd + '/' + file_name;
            //char *ptr, p = ''
            sprintf(query, "select f_id from files where f_owner like '%s' and f_path like '%s';", uname, pwd);
            if(mysql_query(mysql, query)){
                printf("%s\n", mysql_error(mysql));
                return 1;
            }
            result = mysql_store_result(mysql);
            row = mysql_fetch_row(result);
            father_id = atoi(row[0]);
            printf("row[0]: %s\n", row[0]);
            printf("father_id: %d\n", father_id);
            bzero(query, sizeof(query));
            sprintf(query, "insert into files set f_name = '%s', f_exist = 1, f_owner = '%s', f_father = '%d', \
                    f_path = '%s',f_type = 2, f_finsh = 1, f_hash = '%s'", file_name, uname, father_id, path, hash);
            if(mysql_query(mysql, query)){
                printf("%s\n", mysql_error(mysql));
                return 1;
            }
            printf("row[0]: %s\n", row[0]);
            printf("客户端上传完毕\n");
            return 0;
        }
        else{
            bzero(query, sizeof(query));
            sprintf(query, "select * from files where f_hash like '%s';", client_rehash);
            if(mysql_query(mysql, query)){
                printf("%s\n", mysql_error(mysql));
                return 1;
            }
            result = mysql_store_result(mysql);
            row = mysql_fetch_row(result);
            if(row != NULL){
                flag_same = 1;
                send(socketfd, &flag_same, sizeof(int), MSG_NOSIGNAL);        
                return 0;
            }
            strcpy(hash, client_rehash);
        }
        send(socketfd, &flag_same, sizeof(int), MSG_NOSIGNAL);        
    }
    //flag_exist=0说明不存在该文件，执行接收客户端上传文件的逻辑

    char real_path[1024] = "file/";
    strcat(real_path, hash);
    printf("real_path: %s\n", real_path);
    int fd = open(real_path, O_WRONLY|O_CREAT, 0664);
    ERROR_CHECK(fd, -1, "open");
    printf("1\n");
    //获得当前工作目录作为文件的父目录
    bzero(query, sizeof(query));
    int father_id = 0;
    
    sprintf(query, "select f_id from files where f_owner like '%s' and f_path like '%s';", uname, pwd);
    if(mysql_query(mysql, query)){
        printf("%s\n", mysql_error(mysql));
        return 1;
    }
    printf("2\n");
    result = mysql_store_result(mysql);
    row = mysql_fetch_row(result);
    if(row == NULL){
        printf("row = NULL\n");
    }
    father_id = atoi(row[0]);

    printf("3\n");

    //接收客户端要上传的文件的大小
    off_t total_size;
    recv(socketfd, &total_size, sizeof(off_t), MSG_WAITALL);
    printf("total_size: %ld\n",total_size);
    //现有文件大小
    struct stat stat_file;
    fstat(fd, &stat_file);
    printf("file size: %ld\n", stat_file.st_size);    
    //将现有文件大小发给客户端
    send(socketfd, &stat_file.st_size, sizeof(off_t), MSG_NOSIGNAL);
    //如果是第一次接收文件，则新建一条记录
    if(stat_file.st_size == 0){
        bzero(query, sizeof(query));
        sprintf(query, "insert into files set f_name = '%s', f_exist = 1, f_owner = '%s', f_father = '%d', \
            f_path = '%s',f_type = 2, f_finsh = 0, f_hash = '%s'", file_name, uname, father_id, path, hash); 
        if(mysql_query(mysql, query)){
            printf("%s\n", mysql_error(mysql));
            return 1;
        }
    }

    //获取断点
    off_t position = (stat_file.st_size/LENGTH)*LENGTH;
    printf("position: %ld\n", position);
    //改变文件指针的指向到断点
    int ret_lseek = lseek(fd, position, SEEK_SET);
    ERROR_CHECK(ret_lseek, -1, "lseek");
    //接收文件
    while(1){
        int buf_len;
        char buf[5000] = {0};
        recv(socketfd, &buf_len, sizeof(int), MSG_WAITALL);
        if(buf_len == 0){
            fstat(fd, &stat_file);
            if(stat_file.st_size == total_size){
                printf("客户端上传完毕\n");
                //当客户端的文件完全上传，更新文件表中的完成上传标记
                bzero(query, sizeof(query));
                sprintf(query, "update files set f_finsh = 1 where f_owner like '%s' and f_path = '%s' and f_hash like '%s'", uname, path, hash); 
                if(mysql_query(mysql, query)){
                    printf("%s\n", mysql_error(mysql));
                    return 1;
                }
            }
            else{
                printf("客户端断开连接\n");
            }
            break;
        }
        //printf("buf_len: %d\n", buf_len);
        recv(socketfd, buf, buf_len, MSG_WAITALL);
        //printf("buf: %s\n", buf);
        write(fd, buf, buf_len);
    }
    mysql_free_result(result);
    mysql_close(mysql);
    close(fd);
    return 0;
}

